using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace QuickLaunch
{
    public delegate void SettingChangedEventHandler(object sender, SettingChangedEventArgs e);

    public class SettingChangedEventArgs : EventArgs
    {
        private string description;

        public string Description
        {
            get { return this.description; }
            set { this.description = value; }
        }

        public SettingChangedEventArgs(string description)
        {
            this.description = description;
        }
    }

    public class QuickSetting : IDisposable
    {
        #region Properties

        private string rootDir;

        private string fileName;

        private string fullFileName;

        private FileSystemWatcher fsWatcher = null;

        #endregion

        #region Constructor

        public QuickSetting(string rootDir, string fileName)
        {
            if (!rootDir.EndsWith("\\"))
                rootDir += "\\";
            this.rootDir = rootDir;
            this.fileName = fileName;
            this.fullFileName = this.rootDir + this.fileName;
            // check if the file exsits
            if (!File.Exists(this.fullFileName))
            {
                // create the file
                File.Create(this.fullFileName);
                // throw new Exception("error: cannot find the setting file");
            }

            // initialize the file system watcher
            this.InitializeFileWatcher();
        }

        public QuickSetting(string fullFileName)
        {
            // check if the file exists
            if (!File.Exists(fullFileName))
            {
                // create the file
                File.Create(fullFileName);
                // throw new Exception("error: cannot find the setting file");
            }

            // get the root directory and the file name.
            int lastSlash = fullFileName.LastIndexOf('\\');
            if (lastSlash < 0) // using relative directory
            {
                this.rootDir = ".";
                this.fileName = fullFileName;
            }
            else // using absolute directory
            {
                lastSlash++;
                this.rootDir = fullFileName.Substring(0, lastSlash);
                // can't ends with '\'
                this.fileName = fullFileName.Substring(lastSlash, fullFileName.Length - lastSlash);
            }
            this.fullFileName = fullFileName;

            // initialize the file system watcher.
            this.InitializeFileWatcher();
        }

        private void InitializeFileWatcher()
        {
            fsWatcher = new FileSystemWatcher();
            fsWatcher.Path = this.rootDir;
            fsWatcher.Filter = this.fileName;
            fsWatcher.NotifyFilter = NotifyFilters.LastWrite;
            fsWatcher.Changed += new FileSystemEventHandler(fsWatcher_Changed);
            fsWatcher.EnableRaisingEvents = true;
        }

        #endregion

        void fsWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            this.settingItems = null; // clear the cache.
            this.settingTable = null; // clear the cache

            // raise the setting changed event
            if (SettingChanged != null)
                SettingChanged(this, null);
        }

        #region Events

        public event SettingChangedEventHandler SettingChanged;

        #endregion

        private List<string> settingItems = null;

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<string> GetSettingItems()
        {
            if (settingItems == null)
            {
                settingItems = new List<string>();

                try
                {
                    // read the setting file for the root directories.
                    StreamReader reader = new StreamReader(this.fullFileName);
                    string line = reader.ReadLine();
                    while (line != null)
                    {
                        line = line.Trim();
                        // add it into the collection
                        if (!string.IsNullOrEmpty(line))
                            settingItems.Add(line);
                        line = reader.ReadLine();
                    }
                    reader.Close();
                }
                catch (Exception ex)
                {
                    return settingItems;
                }
            }

            return settingItems;
        }

        private Dictionary<string, string> settingTable = null;

        public Dictionary<string, string> GetSettingTable()
        {
            if (settingTable == null)
            {
                List<string> settingItems = GetSettingItems();

                // parse the items to a table (name/value pairs)
                settingTable = new Dictionary<string, string>(settingItems.Count);
                foreach (string item in settingItems)
                {
                    int sepPos = item.IndexOf(':');
                    if (sepPos <= 0 || sepPos + 1 >= item.Length)
                    {
                        if (!settingTable.ContainsKey(item))
                            settingTable.Add(item, "");
                        continue;
                    }

                    string name = item.Substring(0, sepPos).Trim();
                    if (string.IsNullOrEmpty(name))
                        continue;

                    sepPos++;
                    string value = item.Substring(sepPos, item.Length - sepPos).Trim();

                    // add the name/value pair into the table
                    if (!settingTable.ContainsKey(name))
                        settingTable.Add(name, value);
                }
            }

            return settingTable;
        }

        public string GetValue(string name)
        {
            if (this.GetSettingTable().ContainsKey(name))
                return this.GetSettingTable()[name];
            else
                return null;
        }

        public bool SetValue(string name, string value)
        {
            Dictionary<string, string> table = this.GetSettingTable();
            if (table.ContainsKey(name))
            {
                table[name] = value;
                return this.Reset(table);
            }
            else
                return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Add(string item)
        {
            List<string> items = GetSettingItems();
            items.Add(item);
            return Reset(items);
        }

        public bool Add(string name, string value)
        {
            string item = name + ":" + value;
            return this.Add(item);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="items"></param>
        /// <returns></returns>
        public bool Reset(List<string> items)
        {
            // temporarily copy the orginal setting
            File.Copy(this.fullFileName, this.rootDir + this.fileName + ".tmp", true);
            try
            {
                StreamWriter writer = new StreamWriter(this.fullFileName, false);
                foreach (string item in items)
                    writer.WriteLine(item);
                writer.Close();
                return true;
            }
            catch (Exception ex)
            {
                // restore the original setting file
                File.Copy(this.rootDir + this.fileName + ".tmp", this.fullFileName, true);
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        public bool Reset(Dictionary<string, string> settings)
        {
            // parse the setting table into item string list
            List<string> items = new List<string>();
            foreach (string key in settings.Keys)
            {
                string value = settings[key];
                string item = key;
                if (!string.IsNullOrEmpty(value))
                    item += ":" + value;
                items.Add(item);
            }

            return Reset(items);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Delete(string item)
        {
            List<string> items = GetSettingItems();
            if (items.Remove(item))
                return Reset(items);
            return false;
        }

        public bool Delete(string name, string value)
        {
            string item = name + ":" + value;
            return this.Delete(item);
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (this.fsWatcher != null)
                this.fsWatcher.EnableRaisingEvents = false;
        }

        #endregion
    }
}
